package cn.sunline.openapi.utils.sftp;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;

import cfca.sadk.algorithm.common.GenKeyAttribute;
import cfca.sadk.algorithm.common.Mechanism;
import cfca.sadk.lib.crypto.JCrypto;
import cfca.sadk.lib.crypto.Session;
import cfca.sadk.test.TestExt;
import cfca.sadk.util.Signature;
import cfca.sadk.x509.certificate.X509Cert;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;
 
 
/**
 * @author: liyuan  liyuan1@sunline.cn
 * @date: 2019/11/14 15:17
 * @description: 国密SM2算法工具类
 * @version: 3.6
 **/
public class SM2Util2 {
    public static final String PUBLICKEY = "publicKey";
    public static final String PRIVATEKEY = "privateKey";
 
    /**
     * 生成随机秘钥对
     *
     * @return
     */
    public static Map<String, String> generateKeyPair() {
        SM2 sm2 = SM2.Instance();
        AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
        ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
        ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
        BigInteger privateKey = ecpriv.getD();
        ECPoint publicKey = ecpub.getQ();
 
        Map<String, String> map = new HashMap<>();
        map.put(PUBLICKEY, ByteUtils.byteToHex(publicKey.getEncoded()));
        map.put(PRIVATEKEY, ByteUtils.byteToHex(privateKey.toByteArray()));
        return map;
    }
 
    /**
     * 数据加密
     *
     * @param publicKey
     * @param data
     * @return
     * @throws IOException
     */
    public static String encrypt(byte[] publicKey, byte[] data) throws IOException {
        if (publicKey == null || publicKey.length == 0) {
            return null;
        }
 
        if (data == null || data.length == 0) {
            return null;
        }
 
        byte[] source = new byte[data.length];
        System.arraycopy(data, 0, source, 0, data.length);
 
        CipherGw cipher = new CipherGw();
        SM2 sm2 = SM2.Instance();
        ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);
 
        ECPoint c1 = cipher.Init_enc(sm2, userKey);
        cipher.Encrypt(source);
        byte[] c3 = new byte[32];
        cipher.Dofinal(c3);
 
//      System.out.println("C1 " + ByteUtils.byteToHex(c1.getEncoded()));
//      System.out.println("C2 " + ByteUtils.byteToHex(source));
//      System.out.println("C3 " + ByteUtils.byteToHex(c3));
        //C1 C2 C3拼装成加密字串
        return ByteUtils.byteToHex(c1.getEncoded()) + ByteUtils.byteToHex(source) + ByteUtils.byteToHex(c3);
 
    }
 
    /**
     * 数据解密
     *
     * @param privateKey
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] privateKey, String data) throws Exception {
        //byte[] encryptedData) throws IOException {
        if (privateKey == null || privateKey.length == 0) {
            return null;
        }
        // 现有逻辑
        byte[] encryptedData = ByteUtils.hexToByte(data);
        // 原逻辑
/*        if (encryptedData == null || encryptedData.length == 0) {
            return null;
        }
        加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2
        String data = ByteUtils.byteToHex(encryptedData);*/
        /***分解加密字串
         * （C1 = C1标志位2位 + C1实体部分128位 = 130）
         * （C3 = C3实体部分64位  = 64）
         * （C2 = encryptedData.length * 2 - C1长度  - C2长度）
         */
        byte[] c1Bytes = ByteUtils.hexToByte(data.substring(0, 130));
        int c2Len = encryptedData.length - 97;
        byte[] c2 = ByteUtils.hexToByte(data.substring(130, 130 + 2 * c2Len));
        byte[] c3 = ByteUtils.hexToByte(data.substring(130 + 2 * c2Len, 194 + 2 * c2Len));
 
        SM2 sm2 = SM2.Instance();
        BigInteger pri = new BigInteger(1, privateKey);
 
        //通过C1实体字节来生成ECPoint
        ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes);
        CipherGw cipher = new CipherGw();
        cipher.Init_dec(pri, c1);
        cipher.Decrypt(c2);
        cipher.Dofinal(c3);
        //返回解密结果
        return c2;
    }
 
    /**
     * sm3摘要
     *
     * @param params
     * @return
     */
    private static byte[] sm3hash(byte[]... params) throws Exception {
        byte[] res = null;
        try {
            res = SM3forSM2.hashBy(join(params));
        } catch (Exception e) {
//            busiLogger.error("sm3 hash failed!", e);
            throw e;
        }
        return res;
    }
 
    /**
     * 字节数组拼接
     *
     * @param params
     * @return
     */
    private static byte[] join(byte[]... params) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] res = null;
        try {
            for (int i = 0; i < params.length; i++) {
                baos.write(params[i]);
            }
            res = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return res;
    }
 
 
    public static void main(String[] args) throws Exception {
        //生成密钥对
        Map<String, String> map = generateKeyPair();
        String prik = map.get(PRIVATEKEY);
        String pubk = map.get(PUBLICKEY);
        System.out.println("公钥：" + pubk);
        System.out.println("私钥：" + prik);
 
        String plainText = "老周加油啊哈哈哈哈哈asdf";
        System.out.println("原始: ");
        System.out.println(plainText);

        byte[] sourceData = plainText.getBytes();
 
        // 国密规范正式私钥
//        String prik = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";
//        // 国密规范正式公钥
//        String pubk = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A";
        System.out.println("加密: ");
        String cipherText = SM2Util2.encrypt(ByteUtils.hexToByte(pubk), sourceData);
        System.out.println(cipherText);
        System.out.println("解密: ");
        plainText = new String(SM2Util2.decrypt(ByteUtils.hexToByte(prik), cipherText));
        System.out.println(plainText);
    }
 
    /**
     * Attached方式pkcs#7
     * 使用长亮核心提供的国密三方包进行验签
     *
     * @param waitVerify
     * @return 获取原始响应报文
     */
    public static byte[] verifyUseCfca(String deviceName, byte[] waitVerify) throws Exception {
        if (StringUtils.isBlank(deviceName)) {
            deviceName = "JSOFT_LIB";
        }
        JCrypto.getInstance().initialize(deviceName, null);
        final Session session = JCrypto.getInstance().openSession(deviceName);
        Signature signature = new Signature();
        boolean flag = signature.p7VerifyMessageAttach(waitVerify, session);
        if (flag) {
            return signature.getSourceData();
        } else {
            throw new RuntimeException("verify failed!");
        }
    }
 
    /**
     * 使用长亮核心提供的国密三方包进行签名
     *
     * @param signDeviceName
     * @param waitSign
     * @return 签名后的报文
     * @throws Exception
     */
    public static byte[] signUseCfca(String signDeviceName, byte[] waitSign) throws Exception {
        if (StringUtils.isBlank(signDeviceName)) {
            signDeviceName = "JSOFT_LIB";
        }
        JCrypto.getInstance().initialize(signDeviceName, null);
        final Session session = JCrypto.getInstance().openSession(signDeviceName);
        Signature signature = new Signature();
        KeyPair signKeypair = session.generateKeyPair(new Mechanism("SM2",
                new GenKeyAttribute(false, 1)), 256);
        final X509Cert signCert = TestExt.buildX509CertGenerator(signKeypair.getPrivate(),
                signKeypair.getPublic(), session);
        final PrivateKey signPrivateKey = signKeypair.getPrivate();
        byte[] bs = signature.p7SignMessageAttach("sm3WithSM2Encryption", waitSign,
                signPrivateKey, signCert, session);
        return bs;
    }
 
 
}